mov eax,cs
mov ss,eax
mov sp,0x7c00

;计算GDT所在逻辑段的地址
mov eax,[cs:pgdt+0x7c00+0x02]
xor edx,edx
mov ebx,16
div ebx

mov ds,eax  ; eax是段地址
mov ebx,edx ; edx是偏移地址

mov dword [ebx+0x00],0x0000_0000
mov dword [ebx+0x04],0x0000_0000

mov dword [ebx+0x08],0x0000_ffff
mov dword [ebx+0x0c],0x00cf_9200

mov dword [ebx+0x10],0x7c00_01ff
mov dword [ebx+0x14],0x0040_9800

;创建以上代码段的别名描述符(这是一个数据段)
mov dword [ebx+0x18],0x7c0001ff    ;基地址为0x00007c00，512字节
mov dword [ebx+0x1c],0x00409200    ;粒度为1个字节，数据段描述符

mov dword [ebx+0x20],0x7c00fffe
mov dword [ebx+0x24],0x00cf9600

; 初始化GDTR
mov word [cs:pgdt+0x7c00],39   ;GDT的界限=total bytes-1
lgdt [cs:pgdt+0x7c00]  ; load GDT

in al,0x92
or al,0000_0010B
out 0x92,al     ;通过南桥芯片ICH打开A20地址线

cli   ;保护模式下中断机制与实模式不同，没有建立新的中断机制之前，可以先禁止中断

mov eax,cr0
or eax,1   ; 将cr0的0位设为1，标志着进入保护模式
mov cr0,eax

; 从这里开始进入保护模式
jmp dword 0x0010:flush   ; 这里的0008是段选择子

[bits 32]
flush:
mov eax,0x0018                      
mov ds,eax

mov eax,0x0008
mov es,eax
mov gs,eax
mov fs,eax

mov eax,0x0020
mov ss,eax
xor esp,esp

mov dword [es:0x0b8000],0x072e0750 ;字符'P'、'.'及其显示属性
mov dword [es:0x0b8004],0x072e074d ;字符'M'、'.'及其显示属性
mov dword [es:0x0b8008],0x07200720 ;两个空白字符及其显示属性
mov dword [es:0x0b800c],0x076b076f ;字符'o'、'k'及其显示属性

mov ecx,pgdt-string-1 ;i=0;i<length-1;i++
@0:
mov ebx,0
push ecx  ; 暂存外层循环子
@1:
mov ax, [string+ebx];j=i;j<length;j++  每次从内存取出两个字节
cmp ah,al
jge @2
xchg ah,al
mov [string+ebx],ax  ;交换后再写回内存里，看起来不算是很高效
@2:
inc ebx
loop @1
pop ecx
loop @0

mov ecx,pgdt-string
xor ebx,ebx
@4:
mov ah,0x07
mov al,[string+ebx]
mov [es:0xb80a0+ebx*2],ax
inc ebx
loop @4

hlt

string: db 's0ke4or92xap3fv8giuzjcy5l1m7hd6bnqtw.'
;-------------------------------------------------------------------------------
     pgdt             dw 0
                      dd 0x00007e00      ;GDT的物理地址
;-------------------------------------------------------------------------------                             
     times 510-($-$$) db 0
                      db 0x55,0xaa